﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.IO;
using Utils;

namespace GraphicsServer
{
    class SubscriberService
    {
        public event EventHandler<TextEventArgs> GetUpdate;
        private int _port;
        public void StartSubscriberService(int port)
        {
            _port = port;
            var th = new Thread(HostSubscriberService) { IsBackground = true };
            th.Start();
        }


        private readonly List<Point3D> _startPos = new List<Point3D>();

        private void HostSubscriberService()
        {
            _startPos.Add(new Point3D(-25, 5, -25));
            _startPos.Add(new Point3D(25, 5, -25));
            _startPos.Add(new Point3D(25, 5, 25));
            _startPos.Add(new Point3D(-25, 5, 25));

            IPAddress ipV4 = PublisherService.ReturnMachineIP();

            var localEP = new IPEndPoint(ipV4, _port);
            var server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            server.Bind(localEP);

            StartListening(server);
        }

        private void StartListening(Socket server)
        {
            EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
            while (true)
            {
                var data = new byte[1024];
                int recv = server.ReceiveFrom(data, ref remoteEP);

                string messageSendFromClient = Encoding.ASCII.GetString(data, 0, recv);

                if (string.IsNullOrEmpty(messageSendFromClient)) continue;

                switch (messageSendFromClient)
                {
                    case "Subscribe":
                        SubscriberList.AddSubscriber(remoteEP);

                        if (GetUpdate != null)
                            GetUpdate(this, new TextEventArgs("New Subscription\r\n"));
                        break;
                    case "UnSubscribe":
                        SubscriberList.RemoveSubscriber(remoteEP);
                        if (GetUpdate != null)
                            GetUpdate(this, new TextEventArgs("UnSubscription\r\n"));
                        break;
                }
            }
        }

    }

    public class PublisherService
    {
        private int _port;
        public void StartPublisherService(int port)
        {
            _port = port;
            var th = new Thread(HostPublisherService) { IsBackground = true };
            th.Start();
        }

        private void HostPublisherService()
        {
            var ipV4 = ReturnMachineIP();
            var localEP = new IPEndPoint(ipV4, _port);
            var server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            server.Bind(localEP);

            StartListening(server);
        }

        public static IPAddress ReturnMachineIP()
        {
            String hostName = Dns.GetHostName();
            IPHostEntry ipEntry = Dns.GetHostEntry(hostName);
            IPAddress[] addr = ipEntry.AddressList;
            IPAddress ipV4 = null;
            foreach (IPAddress item in addr)
            {
                if (item.AddressFamily == AddressFamily.InterNetwork)
                {
                    ipV4 = item;
                    break;
                }

            }
            if (ipV4 == null)
            {
                MessageBox.Show("You have no IP of Version 4.Server can not run witout it");
                Application.Exit();
            }
            return ipV4;
        }

        private static void StartListening(Socket server)
        {
            EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
            while (true)
            {
                try
                {
                    var data = new byte[1024];
                    server.ReceiveFrom(data, ref remoteEP);

                    List<EndPoint> subscriberListForThisTopic = SubscriberList.GetSubscribers();
                    var workerThreadParameters = new WorkerThreadParameters
                    {
                        Server = server,
                        Data = data,
                        SubscriberListForThisTopic = subscriberListForThisTopic
                    };

                    ThreadPool.QueueUserWorkItem(Publish, workerThreadParameters);
                }
                catch
                { }
            }
        }

        private static void Publish(object stateInfo)
        {
            var workerThreadParameters = (WorkerThreadParameters)stateInfo;
            var server = workerThreadParameters.Server;
            byte[] data = workerThreadParameters.Data;
            var subscriberListForThisTopic = workerThreadParameters.SubscriberListForThisTopic;

            if (subscriberListForThisTopic == null) return;

            foreach (EndPoint endPoint in subscriberListForThisTopic)
            {
                server.SendTo(data, data.Length, SocketFlags.None, endPoint);
            }
        }
    }

    class WorkerThreadParameters
    {
        public Socket Server { get; set; }
        public byte[] Data { get; set; }
        public List<EndPoint> SubscriberListForThisTopic { get; set; }
    }

    public class TextEventArgs : EventArgs
    {
        public string Text { get; set; }

        public TextEventArgs(string text)
        {
            Text = text;
        }
    }

}
